/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
/*
 * Created on Feb 2, 2005
 *
 */
package edu.mit.csail.relo.modelBridge;

import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.log4j.Logger;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.sesame.sail.StatementIterator;

import edu.mit.csail.relo.ReloCore;
import edu.mit.csail.relo.ReloPlugin;
import edu.mit.csail.relo.store.ReloRdfRepository;
import edu.mit.csail.relo.utils.DbgRes;

/**
 * @author vineet
 *
 * Should be identical to an RDF Resource, except that the term Resource is 
 * ambiguous.
 * 
 * Classes should not be inheriting from this this class, unless to provide an 
 * extension to the basic repository based functionality. Extensions are still
 * instantiated from the store.
 * 
 * TODO: review classes inheriting from here
 * 
 */
public class Artifact {

    static final Logger logger = ReloPlugin.getLogger(Artifact.class);
    
	public Resource elementRes = null;

	public Artifact(Resource _elementRes) {
		elementRes = _elementRes;
		if (elementRes == null) throw new IllegalArgumentException();
	}

    /**
     * Used when a default constructor is used to create the class
     * 
     */
    public void init(Resource modelRes) {
        elementRes = modelRes;
        if (elementRes == null) throw new IllegalArgumentException();
    }

    public Artifact getNonDerivedBaseArtifact() {
        return this;
    }
	
	DbgRes tag = new DbgRes(Artifact.class, this);
	public String getTrailer() {
	    return tag.getTrailer();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
    public int hashCode() {
		return elementRes.hashCode();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
    public boolean equals(Object arg0) {
		if (!(arg0 instanceof Artifact)) {
			return false;
		}
		return elementRes.equals(((Artifact) arg0).elementRes);
	}

	@Override
    public String toString() {
	    return elementRes.toString();
	}
	

	
	public Resource getType(ReloRdfRepository repo) {
	    try {
		    return (Resource) repo.getRequiredProperty(elementRes, repo.rdfType).getObject();
	    } catch (Exception e) {
	        dumpProperties(repo, elementRes);
	        logger.error("Element: " + elementRes + " does not have a type.", e);
	        return null;
	    }
	}

    public static void dumpProperties(ReloRdfRepository rdfModel, Resource elementRes) {
        logger.info("Properties for: " + elementRes);
        StatementIterator si = rdfModel.getStatements(elementRes, null, (Value) null);
	    while (si.hasNext()) {
	        org.openrdf.model.Statement stmt = si.next();
	        logger.info("   " + stmt.getPredicate() + "  " + stmt.getObject());
	    }
        si.close();
    }

    
    // deal with containment heirarchy
    // reqd. properties: name, contains
	public String getName(ReloRdfRepository repo) {
		if (elementRes == null) {
			return "{null}";
		}

		return repo.getRequiredLiteral(elementRes, ReloCore.name);
	}


	/**
	 * Note the input collection is changed by the method
	 */
	@SuppressWarnings("unchecked")
    protected static List<Artifact> transformResourcesToArtifacts(List<Resource> in) {
        CollectionUtils.transform(in, new Transformer() {
            public Object transform(Object arg0) {
                return new Artifact((Resource)arg0);
            }});
        return (List<Artifact>) (List) in;
	}
	
	public Artifact getParentArtifact(ReloRdfRepository repo) {
		//TODO for inner class
		//http://relo.csail.mit.edu/rdf/jdt-wkspc#edu.mit.csail.relo.jdt.actions$OpenForBrowsingAction$1.runInUIThread(IProgressMonitor,)
        Resource parentRes = null /*(Resource) repo.getStatement(elementRes, ReloCore.cachedRevContains, null).getObject()*/;
        if (parentRes == null) parentRes = repo.getStatement((Resource)null, ReloCore.contains, elementRes).getSubject();
	    if (parentRes == null) return null;
        return new Artifact(parentRes);
	}
	
	// @tag design-issue: It might be nice to filter here to make sure that all
	// the return types are creatable - but then what about those that want to
	// just 'browse the model'
	public List<Artifact> getChildrenArtifacts(ReloRdfRepository repo) {
		return getChildrenArtifacts(repo, null);
	}

    public List<Artifact> getChildrenArtifacts(ReloRdfRepository repo, Predicate filterPred) {
        return listArt(repo, DirectedRel.getFwd(ReloCore.contains), filterPred);
	}
    
    public List<Artifact> listArt(ReloRdfRepository repo, DirectedRel rel, Predicate filter) {
        List<Resource> retValAsResource = new LinkedList<Resource> ();
        if (rel.isFwd)
            repo.getResourcesFor(retValAsResource, elementRes, rel.res, null);
        else
            repo.getResourcesFor(retValAsResource, null, rel.res, elementRes);
        CollectionUtils.filter(retValAsResource, filter);
        return transformResourcesToArtifacts(retValAsResource);
    }


    public List<Artifact> listArt(ReloRdfRepository repo, DirectedRel rel) {
        return listArt(repo, rel, null);
    }

    public List<Artifact> listConnectedArtifacts(ReloRdfRepository repo, Set<URI> filteredPredicates) {
        List<Artifact> retValArt = new LinkedList<Artifact> ();
        StatementIterator si;

        si = repo.getStatements(elementRes, null, null);
        while (si.hasNext()) {
            Statement stmt = si.next();
            
            if (filteredPredicates.contains(stmt.getPredicate())) continue;
            if (!(stmt.getObject() instanceof Resource)) continue;
                
            retValArt.add(new Artifact((Resource) stmt.getObject()));
        }
        si.close();

        si = repo.getStatements(null, null, elementRes);
        while (si.hasNext()) {
            Statement stmt = si.next();
            
            if (filteredPredicates.contains(stmt.getPredicate())) continue;
            if (!(stmt.getSubject() instanceof Resource)) continue;
                
            retValArt.add(new Artifact((Resource) stmt.getSubject()));
        }
        si.close();

        return retValArt;
    }

    
}
